import random
from abc import ABCMeta, abstractmethod
from tqdm import tqdm
import concurrent.futures

ROW = COL = 3  # 棋盘大小
SPACE = "-"  # 空格标签
HUMAN = "HUMAN"  # 人类棋子标签
COMPUTER = "COMPUTER"  # 电脑棋子标签


# 棋盘是否有空位
def empty(board):
    return SPACE in board


# 判断player是否胜利
def winner(board, player):
    wins = [
        [board[0], board[1], board[2]],
        [board[3], board[4], board[5]],
        [board[6], board[7], board[8]],
        [board[0], board[3], board[6]],
        [board[1], board[4], board[7]],
        [board[2], board[5], board[8]],
        [board[0], board[4], board[8]],
        [board[2], board[4], board[6]],
    ]
    state = [player, player, player]
    return state in wins


# 定义抽象基类
class Player(metaclass=ABCMeta):
    def __init__(self, chess):
        self.chess = chess

    @abstractmethod
    def move(self, board):
        pass


class Computer(Player):
    def __init__(self, chess="O"):
        super().__init__(chess)

    def minimax(self, board, player, next_player, alpha=-2, beta=2):
        if winner(board, self.chess):
            return +1
        if winner(board, "X" if self.chess == "O" else "O"):
            return -1
        elif not empty(board):
            return 0  # 平局

        for move in range(ROW * COL):
            if board[move] == SPACE:  # 尝试下棋
                board[move] = player  # 记录
                val = self.minimax(
                    board, next_player, player, alpha, beta
                )  # 继续思考对手怎么下棋
                board[move] = SPACE  # 重置

                if player == self.chess:  # 极大 max value
                    if val > alpha:
                        alpha = val
                    if alpha >= beta:  # 剪枝
                        return beta
                else:  # 极小 min value
                    if val < beta:
                        beta = val
                    if beta <= alpha:  # 剪枝
                        return alpha

        return alpha if player == self.chess else beta

    def move(self, board):
        best = -2
        my_moves = []
        for move in range(ROW * COL):
            if board[move] == SPACE:  # 尝试下棋
                board[move] = self.chess  # 记录
                score = self.minimax(
                    board, "X" if self.chess == "O" else "O", self.chess
                )  # 思考对手怎么下棋
                board[move] = SPACE  # 重置

                if score > best:  # 找到更优的位置
                    best = score
                    my_moves = [move]
                if score == best:  # 一样优秀的位置
                    my_moves.append(move)

        pos = random.choice(my_moves)  # 随机挑出一个位置
        board[pos] = self.chess


class Game:
    def __init__(self):
        self.board = [SPACE] * (ROW * COL)
        self.computer1 = Computer(chess="X")
        self.computer2 = Computer(chess="O")
        self.players = [self.computer1, self.computer2]
        self.current_player = self.players[0]

    def switch(self):
        self.current_player = self.players[
            (self.players.index(self.current_player) + 1) % 2
        ]

    def render(self):
        print("--------------")
        for i in range(ROW):
            for j in range(COL):
                k = i * ROW + j
                print(f"| {self.board[k]} ", end="")
            print("|")
        print("--------------")

    def start(self):
        while True:
            self.current_player.move(self.board)
            if winner(self.board, self.computer1.chess):
                return "Computer1"
            elif winner(self.board, self.computer2.chess):
                return "Computer2"
            elif not empty(self.board):
                return "Draw"
            self.switch()


def simulate_games_chunk(num_games, progress_bar):
    results = {"Computer1": 0, "Computer2": 0, "Draw": 0}
    for _ in range(num_games):
        game = Game()
        result = game.start()
        results[result] += 1
        progress_bar.update(1)
    return results


def simulate_games(num_games, num_threads):
    chunk_size = num_games // num_threads
    futures = []
    results = {"Computer1": 0, "Computer2": 0, "Draw": 0}

    with concurrent.futures.ThreadPoolExecutor(max_workers=num_threads) as executor:
        with tqdm(total=num_games, desc="总进度") as pbar:
            for _ in range(num_threads):
                futures.append(executor.submit(simulate_games_chunk, chunk_size, pbar))

            for future in concurrent.futures.as_completed(futures):
                chunk_results = future.result()
                for key in results:
                    results[key] += chunk_results[key]

    return results


if __name__ == "__main__":
    num_games = 10000
    num_threads = 4  # 调整线程数以匹配你的CPU核心数量
    results = simulate_games(num_games, num_threads)
    print(f"在 {num_games} 次模拟中：")
    print(f"电脑1胜利次数: {results['Computer1']}")
    print(f"电脑2胜利次数: {results['Computer2']}")
    print(f"平局次数: {results['Draw']}")
